<?php

//1.
//$currentDate = $moment->format('y-m-d');
//2. 坏
/*getUserInfo();
getUserData();
getUserRecord();
getUserProfile();*/
//好
/*getUser();*/

//demo1
function isShopOpen(string $day): bool
{
	if (empty($day)) {
		return false;
	}
	$openingDays = [
		'friday', 'saturday', 'sunday'
	];
	return in_array(strtolower($day), $openingDays);
}
//避免深层嵌套，尽早返回 (part 2)
function fibonacci(int $n): int
{
	if ($n === 0 || $n === 1) {
		return $n;
	}
	if ($n > 50) {
		throw new Exception('Not supported');
	}
	return fibonacci($n - 1) + fibonacci($n - 2);
}

//少用无意义的变量名
$locations = ['Austin', 'New York', 'San Francisco'];
foreach ($locations as $location) {
	doStuff();
    doSomeOtherStuff();
    dispatch($location);
}

//6不要添加不必要上下文
class Car
{
	public $carMake;
	public $carModel;
	public $carColor;
}

class Car
{
    public $make;
    public $model;
    public $color;

    //...
}
//7合理使用参数默认值，没必要在方法里再做默认值检测
function createMicrobrewery(string $breweryName = 'Hipster Brew Co.'): void
{
    // ...
}
//8  表达式 使用恒等式
//不好 简易对比会将字符串转为整形
$a = '42';
$b = 42;
if ($a != $b) {
	 //这里始终执行不到
}
//好
$a = '42';
$b = 42;

if ($a !== $b) {
    // The expression is verified
}
//9函数 函数参数（最好少于2个）
//坏:
function createMenu(string $title, string $body, string $buttonText, bool $cancellable){

}
//好
class MenuConfig
{
	public $title;
	public $body;
	public $buttonText;
	public $cancellable = false;
}
$config = new MenuConfig();
$config->title = 'Foo';
$config->body = 'Bar';
$config->buttonText = 'Baz';
$config->cancellable = ture;

function createMenu1(MenuConfig $config): void
{
	//
}
//函数应该只做一件事
/**
这是迄今为止软件工程里最重要的一个规则。当一个函数做超过一件事的时候，他们就难于实现、测试和理解。当你把一个函数拆分到只剩一个功能时，他们就容易被重构，然后你的代码读起来就更清晰。如果你光遵循这条规则，你就领先于大多数开发者了。
**/
//坏
function emailClientsBad(array $clients)
{
	foreach ($clients as $client) {
		$clientRecord = $db->find($client);
		if ($clientRecord->isActive()) {
			email($client);
		}
	}
}

//好
function emailClientsGood(array $clients)
{
	$activeClients = activeClients($clients);
	array_walk($activeClients, 'email');
}

function activeClients(array $clients)
{
	return array_filter($clients, 'isClientActive');
}

function isClientActive(int $client)
{
	$clientRecord = $db->find($client);
	return $clientRecord->isActive();
}

//11 函数名应体现他做了什么事
//坏:
/*class Email
{
    //...

    public function handle(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// 啥？handle处理一个消息干嘛了？是往一个文件里写吗？
$message->handle();
好:
class Email 
{
    //...

    public function send(): void
    {
        mail($this->to, $this->subject, $this->body);
    }
}

$message = new Email(...);
// 简单明了
$message->send();
*/

//12 函数里应当只有一层抽象abstraction
/*
当你抽象层次过多时时，函数处理的事情太多了。需要拆分功能来提高可重用性和易用性，以便简化测试。 （译者注：这里从示例代码看应该是指嵌套过多）
*/
//bad
function parseBetterJSAlternative(string $code)
{
	$regexes = [];
	$statements = explode(' ', $code);
	$tokens = [];
	foreach($regexes as $regex)
	{
		foreach($statements as $statement)
		{

		}
	}
	$ast = [];
	foreach($tokens as $token)
	{

	}
	foreach($ast as $node)
	{

	}
}
//好:
class Tokenizer
{
    public function tokenize(string $code): array
    {
        $regexes = [
            // ...
        ];

        $statements = explode(' ', $code);
        $tokens = [];
        foreach ($regexes as $regex) {
            foreach ($statements as $statement) {
                $tokens[] = /* ... */;
            }
        }

        return $tokens;
    }
}

class Lexer
{
    public function lexify(array $tokens): array
    {
        $ast = [];
        foreach ($tokens as $token) {
            $ast[] = /* ... */;
        }

        return $ast;
    }
}

class BetterJSAlternative
{
    private $tokenizer;
    private $lexer;

    public function __construct(Tokenizer $tokenizer, Lexer $lexer)
    {
        $this->tokenizer = $tokenizer;
        $this->lexer = $lexer;
    }

    public function parse(string $code): void
    {
        $tokens = $this->tokenizer->tokenize($code);
        $ast = $this->lexer->lexify($tokens);
        foreach ($ast as $node) {
            // 解析逻辑...
        }
    }
}
//不要用flag作为函数的参数

//bad

function createFile(string $name, bool $temp = false): void
{
    if ($temp) {
        touch('./temp/'.$name);
    } else {
        touch($name);
    }
}
//good
function createFile(string $name): void
{
    touch($name);
}

function createTempFile(string $name): void
{
    touch('./temp/'.$name);
}

/*
避免副作用

一个函数做了比获取一个值然后返回另外一个值或值们会产生副作用如果。副作用可能是写入一个文件，修改某些全局变量或者偶然的把你全部的钱给了陌生人。

现在，你的确需要在一个程序或者场合里要有副作用，像之前的例子，你也许需要写一个文件。你想要做的是把你做这些的地方集中起来。不要用几个函数和类来写入一个特定的文件。用一个服务来做它，一个只有一个。

重点是避免常见陷阱比如对象间共享无结构的数据，使用可以写入任何的可变数据类型，不集中处理副作用发生的地方。如果你做了这些你就会比大多数程序员快乐。

不要用 global 声明全局变量

*/

/*
不要写全局函数
在大多数语言中污染全局变量是一个坏的实践，因为你可能和其他类库冲突 并且调用你api的人直到他们捕获异常才知道踩坑了。让我们思考一种场景： 如果你想配置一个数组，你可能会写一个全局函数config()，但是他可能 和试着做同样事的其他类库冲突。
*/

//bad
function config(): array
{
    return  [
        'foo' => 'bar',
    ]
}

//good

class Configuration
{
    private $configuration = [];

    public function __construct(array $configuration)
    {
        $this->configuration = $configuration;
    }

    public function get(string $key): ?string
    {
        return isset($this->configuration[$key]) ? $this->configuration[$key] : null;
    }
}


//不要使用单例模式

//封装条件语句
//bad
if ($article->state === 'published') {
    // ...
}

//good
if ($article->isPublished()) {
    // ...
}
//避免用反义条件判断
//bad
function isDOMNodeNotPresent(\DOMNode $node): bool
{
    // ...
}

if (!isDOMNodeNotPresent($node))
{
    // ...
}
//good
function isDOMNodePresent(\DOMNode $node): bool
{
    // ...
}

if (isDOMNodePresent($node)) {
    // ...
}

//避免条件判断
/*
这看起来像一个不可能任务。当人们第一次听到这句话是都会这么说。 "没有if语句我还能做啥？" 答案是你可以使用多态来实现多种场景 的相同任务。第二个问题很常见， “这么做可以，但为什么我要这么做？” 答案是前面我们学过的一个Clean Code原则：一个函数应当只做一件事。 当你有很多含有if语句的类和函数时,你的函数做了不止一件事。 记住，只做一件事。
*/
//避免类型检查 (part 1)
//bad
function travelToTexas($vehicle): void
{
    if ($vehicle instanceof Bicycle) {
        $vehicle->pedalTo(new Location('texas'));
    } elseif ($vehicle instanceof Car) {
        $vehicle->driveTo(new Location('texas'));
    }
}
//good
 function travelToTexas(Traveler $vehicle)
 {
    $vehicle->travelTo(new Location('texa'));
 }   

//避免类型检查 (part 2)
 //bad
function combine($val1, $val2): int
{
    if (!is_numeric($val1) || !is_numeric($val2)) {
        throw new \Exception('Must be of type Number');
    }

    return $val1 + $val2;
}
//好:
function combine(int $val1, int $val2): int
{
    return $val1 + $val2;
}
/*
移除僵尸代码
僵尸代码和重复代码一样坏。没有理由保留在你的代码库中。如果从来没被调用过，就删掉！ 因为还在代码版本库里，因此很安全。
*/

//对象和数据结构
/*
使用 getters 和 setters
在PHP中你可以对方法使用public, protected, private 来控制对象属性的变更。

当你想对对象属性做获取之外的操作时，你不需要在代码中去寻找并修改每一个该属性访问方法
当有set对应的属性方法时，易于增加参数的验证
封装内部的表示
使用set和get时，易于增加日志和错误控制
继承当前类时，可以复写默认的方法功能
当对象属性是从远端服务器获取时，get*，set*易于使用延迟加载
此外，这样的方式也符合OOP开发中的开闭原则
*/
//坏:
class BankAccount
{
    public $balance = 1000;
}
$bankAccount = new BankAccount();
$bankAccount->balance -= 100;

//good
class BankAccount
{
    private $balance;

    public function __construct(int $balance)
    {
        $this->balance = $balance;
    }

    public function withdraw(int $amount)
    {
        if ($amount > $this->balance) {
            throw new Exception('1221');
        }
        $this->balance -= $amount;
    }

    public function deposit(int $amount)
    {
        $this->balance += $amount;
    }

    public function getBalance()
    {
        return $this->balance;
    }
}
$bankAccount = new BankAccount();
$bankAccount->withraw($shoesPrice);
$balance = $BankAccount->getBalance();

//给对象使用私有或受保护的成员变量

//bad 
class Employee
{
    public $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }
}
$employee = new Employee('Johe Doe');

echo $employee->name();

//good
class Employee
{
    private $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }
}

$employee = new Employee('Johe Doe');
$employee->getName();

/*

类
少用继承多用组合
正如 the Gang of Four 所著的设计模式之前所说， 我们应该尽量优先选择组合而不是继承的方式。使用继承和组合都有很多好处。 这个准则的主要意义在于当你本能的使用继承时，试着思考一下组合是否能更好对你的需求建模。 在一些情况下，是这样的。

接下来你或许会想，“那我应该在什么时候使用继承？” 答案依赖于你的问题，当然下面有一些何时继承比组合更好的说明：

你的继承表达了“是一个”而不是“有一个”的关系（人类-》动物，用户-》用户详情）
你可以复用基类的代码（人类可以像动物一样移动）
你想通过修改基类对所有派生类做全局的修改（当动物移动时，修改她们的能量消耗）
*/
//bad
class Employee
{
    private $name;
    private $email;

    public function __construct(string $name, string $email)
    {
        $this->name = $name;
        $this->email = $email;
    }
}

class EmployeeTaxData extends Employee 
{
    private $ssn;
    private $salary;
    
    public function __construct(string $name, string $email, string $ssn, string $salary)
    {
        parent::__construct($name, $email);

        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}
//good
class EmployeeTaxData 
{
    private $ssn;
    private $salary;

    public function __construct(string $ssn, string $salary)
    {
        $this->ssn = $ssn;
        $this->salary = $salary;
    }

    // ...
}

class Employee
{
    private $name;
    private $email;

    public function __construct(string $name)
    {
        $this->name = $name;
        $this->email = $email;
    }

    public function setTaxData(string $ssn, string $salary)
    {
        $this->setTaxData = new EmployeeTaxData($ssn, $salary);
    }
}

//避免连贯接口

/*
连贯接口Fluent interface是一种 旨在提高面向对象编程时代码可读性的API设计模式，他基于方法链Method chaining

有上下文的地方可以降低代码复杂度，例如PHPUnit Mock Builder 和Doctrine Query Builder ，更多的情况会带来较大代价：

While there can be some contexts, frequently builder objects, where this pattern reduces the verbosity of the code (for example the PHPUnit Mock Builder or the Doctrine Query Builder), more often it comes at some costs:

破坏了 对象封装
破坏了 装饰器模式
在测试组件中不好做mock
导致提交的diff不好阅读
了解更多请阅读 连贯接口为什么不好 ，作者 Marco Pivetta.
*/
//bad
class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';

    public function setMake(string $make): self
    {
        $this->make = $make;

        // NOTE: Returning this for chaining
        return $this;
    }

    public function setModel(string $model): self
    {
        $this->model = $model;

        // NOTE: Returning this for chaining
        return $this;
    }

    public function setColor(string $color): self
    {
        $this->color = $color;

        // NOTE: Returning this for chaining
        return $this;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}

$car = (new Car())
  ->setColor('pink')
  ->setMake('Ford')
  ->setModel('F-150')
  ->dump();

  class Car
{
    private $make = 'Honda';
    private $model = 'Accord';
    private $color = 'white';

    public function setMake(string $make): void
    {
        $this->make = $make;
    }

    public function setModel(string $model): void
    {
        $this->model = $model;
    }

    public function setColor(string $color): void
    {
        $this->color = $color;
    }

    public function dump(): void
    {
        var_dump($this->make, $this->model, $this->color);
    }
}
$car = new Car();
$car->setColor('pink');
$car->setMake('Ford');
$car->setModel('F-150');
$car->dump();
/*
推荐使用 final 类
能用时尽量使用 final 关键字:

阻止不受控的继承链
鼓励 组合.
鼓励 单一职责模式.
鼓励开发者用你的公开方法而非通过继承类获取受保护方法的访问权限.
使得在不破坏使用你的类的应用的情况下修改代码成为可能.
The only condition is that your class should implement an interface and no other public methods are defined.

For more informations you can read the blog post on this topic written by Marco Pivetta (Ocramius).
*/

//bad
final class Car
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * @return string The color of the vehicle
     */
    public function getColor() 
    {
        return $this->color;
    }
}
//good
interface Vehicle
{
    /**
     * @return string The color of the vehicle
     */
    public function getColor();
}

final class Car implements Vehicle
{
    private $color;
    
    public function __construct($color)
    {
        $this->color = $color;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getColor() 
    {
        return $this->color;
    }
}